/*
 * 
 *    ┏┓　　　┏┓
 *  ┏┛┻━━━┛┻┓
 *  ┃　　　　　　　┃
 *  ┃　　　━　　　┃
 *  ┃　＞　　　＜　┃
 *  ┃　　　　　　　┃
 *  ┃...　⌒　...　┃
 *  ┃　　　　　　　┃
 *  ┗━┓　　　┏━┛
 *      ┃　　　┃　
 *      ┃　　　┃
 *      ┃　　　┃
 *      ┃　　　┃  神兽保佑
 *      ┃　　　┃  代码无bug　　
 *      ┃　　　┃
 *      ┃　　　┗━━━┓
 *      ┃　　　　　　　┣┓
 *      ┃　　　　　　　┏┛
 *      ┗┓┓┏━┳┓┏┛
 *        ┃┫┫　┃┫┫
 *        ┗┻┛　┗┻┛
 */

/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2022-06-26 19:02:43
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2022-10-06 21:02:56
 * @FilePath: /imx6ull-app/15_dtsplatform/15_dtsledplatform.c
 * @Description: platform框架+设备树
 */
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"

#include "linux/types.h"
#include "linux/kernel.h"
#include "linux/ide.h"

#include "linux/fs.h"
#include "linux/of_gpio.h"
#include "linux/cdev.h"

#define NAME    "dtsled_platform"

struct ST_DTSLED
{
   dev_t    dev_id;
   int major;
   int minor;

   struct cdev st_cdev;
   struct class *st_class;
   struct device *st_device;

   struct device_node *st_node;

   unsigned int led_gpio;
};

struct ST_DTSLED st_dtsled;

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: led_open
* function: 
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
int led_open(struct inode *nd, struct file *fp)
{
    fp->private_data = &st_dtsled;
    return 0;
}

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: led_write
* function: 
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
ssize_t led_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{
    int retval = 0;
    unsigned char databuf[2];
    unsigned char ledstat;
    retval = copy_from_user(databuf,buf,len);
    if (retval < 0) 
    {
        printk("write failed\r\n");
        return -1;
    }
    
    ledstat = databuf[0];

    if (ledstat)
    {
        gpio_set_value(st_dtsled.led_gpio,0);
    }
    else
    {
        gpio_set_value(st_dtsled.led_gpio,1);
    }

    return 0;
}

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: led_release
* function: 
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
int led_release(struct inode *nd, struct file *fp)
{
    return 0;
}


static struct file_operations fops_dtsled = {
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_write,
    .release = led_release,
};

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: dtsled_probe
* function: 
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
int dtsled_probe(struct platform_device *pf_dev)
{
    //查找设备树
    st_dtsled.st_node = of_find_node_by_path("/lux_ledgpio");
    if (st_dtsled.st_node == NULL)
    {
        printk("lux_ledgpio not found\r\n");
        return -1;
    }
    st_dtsled.led_gpio = of_get_named_gpio(st_dtsled.st_node,"led-gpio",0);
    if (st_dtsled.led_gpio < 0)
    {
        printk("led-gpio not found\r\n");
        return -1;
    }
    
    gpio_request(st_dtsled.led_gpio,"led0");
    gpio_direction_output(st_dtsled.led_gpio,1);//设置为输出，默认高电平

    //向内核添加字符设备
    alloc_chrdev_region(&st_dtsled.dev_id,0,1,NAME);
    st_dtsled.major = MAJOR(st_dtsled.dev_id);
    st_dtsled.minor = MINOR(st_dtsled.dev_id);
    printk("major:%d    minor:%d\r\n",st_dtsled.major,st_dtsled.minor);

    st_dtsled.st_cdev.owner = THIS_MODULE;
    cdev_init(&st_dtsled.st_cdev,&fops_dtsled);
    cdev_add(&st_dtsled.st_cdev,st_dtsled.dev_id,1);

    st_dtsled.st_class = class_create(THIS_MODULE,NAME);
    device_create(st_dtsled.st_class,NULL,st_dtsled.dev_id,NULL,NAME);
    return 0;
}

int dtsled_remove(struct platform_device *pf_dev)
{
    gpio_set_value(st_dtsled.led_gpio,1);//卸载默认高点平
    cdev_del(&st_dtsled.st_cdev);
    unregister_chrdev_region(st_dtsled.dev_id,1);
    device_destroy(st_dtsled.st_class,st_dtsled.dev_id);
    class_destroy(st_dtsled.st_class);
    return 0;
}

/*
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};
*/

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: 
* function: 设备和驱动匹配方式
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
static struct of_device_id	led_of_match_table[] = {
    {
        .compatible = "lux_ledgpio_compatible",
    },
    {

    }
};


/*
struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	struct module		
	const char		
	bool suppress_bind_attrs;
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;
	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
	const struct dev_pm_ops *pm;
	struct driver_private *p;
};
*/
struct platform_driver dtsled_platform_driver = {
    .probe = dtsled_probe,
    .remove = dtsled_remove,
    .driver = {
        .name = "lux-dtsled",
        .of_match_table = led_of_match_table,
    },
};

static int __init dtsled_init(void)
{
    /*
    int __platform_driver_register(struct platform_driver *,
					struct module *);
    */
    return platform_driver_register(&dtsled_platform_driver);
}

static void __exit dtsled_exit(void)
{
    /*
    void platform_driver_unregister(struct platform_driver *);
    */
    platform_driver_unregister(&dtsled_platform_driver);
}

module_init(dtsled_init);
module_exit(dtsled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");